
perf = typeof perf == "undefined" ? {} : perf ;
perf.utils = {};

/**
 * ユーティリティ
 */
perf.utils = {
  currentTime: function() {
    return new Date().getTime();
  },
  merge : function( a, b ) {
    for (var i in b) {
     if ( b.hasOwnProperty(i) ) {
       a[i] = b[i];
     }
    }
    return a;
  },
  observe : function( element, event, handler ){
    if (element.addEventListener) {
      element.addEventListener(event, handler, true);
    } else {
      element.attachEvent("on" + event, handler);
    }
  }
}
/**
 * @constructor リスナサポート
 */
perf.utils.ListenerSupport = function() {
  this._listeners = {};
}
perf.utils.ListenerSupport.prototype = {
    
  /**
   * @function イベントリスナを追加する。
   * @param {String}
   *            type イベント種別
   * @param {Function
   *            or Object} listener イベントリスナ
   * @return イベントリスナ
   */
  addListener: function(type, listener) {
      this.getListeners(type).push(listener);
      return listener;
  },
  /**
   * @function イベントリスナを削除する。
   * @param {String}
   *            type イベント種別
   * @param {Function
   *            or Object} listener イベントリスナ
   */
  removeListener: function(type, listener) {
      var tmp = [];
      var list = this.getListeners(type);
      for ( var i = 0, n = list.length; i < n; i++) {
          if(list[i] !== listener) 
              tmp.push(list[i]);
      }
      this._listeners[type] = tmp;
  },
  /**
   * @function イベントリスナをすべて削除する。
   */
  clearListeners: function() {
    this._listeners = {};
  },
  /**
   * @function イベント種別に関連付けられたイベントリスナの配列を取得する。
   * @param {String}
   *            type イベント種別
   * @return イベントリスナの配列
   */
  getListeners: function(type) {
      if(!this._listeners) {
          this._listeners = {};
      }
      if(!this._listeners[type]) {
          this._listeners[type] = [];
      }
      return this._listeners[type];
  },
  
  /**
   * @function イベントを中継する。
   * @param {Object} ev イベント 
   */
  relay : function( ev ) {
    this.fire( ev.eventType, ev );
  },
  
  /**
   * @function イベントを発火する
   * @param {String}
   *            type イベント種別
   * @param {Object}
   *            event イベント
   * @param {Function}
   *            処理関数(オプション)。 指定された場合、登録されたリスナとイベントを引数として関数を呼び出す。
   */
  fire: function(type, event, f) {
      event = event || {};
      event.eventType = type;
      var list = this.getListeners(type);
      for ( var i = 0, n = list.length; i < n; i++) {
          if(f && typeof f === "function") {
              f.call(null, list[i], event);
          } else {
              var handlerName = ["on",type.substring(0,1).toUpperCase(),type.substring(1)].join("");
              if(typeof list[i] === "function") {
                  list[i].call(null, event);
              } else if(typeof list[i][handlerName] === "function") {
                  list[i][handlerName](event);
              } else {
                  throw {
                      msg: "illegal listener",
                      listener: list[i]
                  };
              }
          }
      }
  }
}


perf.utils.Sequence = function() {
  this.list = [];
}
perf.utils.Sequence.prototype = {
  append : function(item) {
    this.list.push( item );
  },
  appendAll : function(items) {
    this.list = this.list.concat( items );
  },
  next : function( context ) {
    if ( this.list.length <= 0 ) return false;
    var item = this.list[0];
    if ( item(context) ) {
      this.list.shift();
      return this.next( context );
    }
    return true;
  }
}
